home *** CD-ROM | disk | FTP | other *** search
/ Gold Medal Software 2 / Gold Medal Software Volume 2 (Gold Medal) (1994).iso / prog / asm_n_z.arj / NJMOVE.ASM < prev    next >
Assembly Source File  |  1989-01-16  |  46KB  |  2,047 lines

  1.     page    60,150
  2.     title    NJMOVE - Nifty James' MOVE Utility
  3.     subttl    (C) 1989 Blaszczak
  4.  
  5. ;
  6. ; Nifty James' Famous Move Utility
  7. ; Version 1.00 of 16 January 1989
  8. ; (C) Copyright 1989 By Mike Blaszczak
  9. ;
  10.  
  11. ;
  12. ; This file is written for the Microsoft Macro Assembler,
  13. ; Version 5.10 or higher.  The program will not properly
  14. ; operate under any DOS version less than 2.00, but will
  15. ; work with any PC-DOS or MS-DOS Versions 2.00 and above.
  16. ; This is an .EXE file, and should not be run through
  17. ; EXE2BIN.  The executable file should be packed, or the
  18. ; LINK option /EXEPACK should be used.
  19. ;
  20.  
  21.  
  22. ; ----------------------------------------------------------------------------
  23. ;    ASCII Equates
  24.  
  25. eos    equ    000h        ; end-of-string character
  26. bell    equ    007h        ; bell beeper
  27. bs    equ    008h        ; back-space
  28. tab    equ    009h        ; tab
  29. lf    equ    00Ah        ; line-feed character
  30. cr    equ    00Dh        ; carriage return
  31. ctrlz    equ    01Ah        ; control zee (aka, EOF)
  32. eof    equ    ctrlz
  33. space    equ    020h        ; blank space
  34.  
  35.  
  36. ; ----------------------------------------------------------------------------
  37. ;    DOS Function Calls and constants
  38.  
  39. INTDOS    equ    021h
  40.  
  41. STDIN    equ    0        ; standard output file handle
  42. STDOUT    equ    1        ; standard input file handle
  43.  
  44. NormalAttrib    equ    000h        ; normal attribute byte (nothing set)
  45. SubDirAttrib    equ    010h        ; attribute for a directory
  46.  
  47. PutChar    equ    002h        ; put character to stdout
  48. GetChar    equ    007h        ; get char from stdin, with no echo
  49. GetDefDisk    equ    019h        ; get current default disk drive
  50. SetDTA    equ    01Ah        ; set the DOS DTA address
  51. FreeSpace    equ    036h        ; get disk free space
  52. CreateHandle    equ    03Ch        ; create a file handle
  53. OpenHandle    equ    03Dh        ; open a file handle
  54. CloseHandle    equ    03Eh        ; close a file handle
  55. ReadHandle    equ    03Fh        ; read from a file or device
  56. WriteHandle    equ    040h        ; write to a file or device
  57. DeleteFile    equ    041h        ; delete a file name
  58. GetAttrib    equ    04300h        ; get file attributes
  59. SetAttrib    equ    04301h        ; set file attributes
  60. IOCTL    equ    044h        ; I/O control/status call
  61. GetDefDir    equ    047h        ; get current default directory
  62. Terminate    equ    04Ch        ; terminate this program
  63. FindFirst    equ    04Eh        ; find first file matching wildcard
  64. FindNext    equ    04Fh        ; find next file matching wildcard
  65. RenameFile    equ    056h        ; rename a file
  66. GetDateTime    equ    05700h        ; get date or time
  67. SetDateTime    equ    05701h        ; set date or time
  68. NormalizePath    equ    060h        ; normalize pathname
  69. GetPSP    equ    062h        ; get the programs PSP
  70.  
  71. ; note that NormalizePath is an undocumented call.  DS:SI points to a
  72. ; path specification which may contain a drive specifier.  ES:DI points
  73. ; to a buffer for the normalized pathname.  The file simply fixes the
  74. ; "." and ".." references in the file, making the pathname as short as
  75. ; possible.  This expansion is simply used so the user knows *exactly*
  76. ; where the file is coming from.  Microsoft, in their infinite (heh)
  77. ; wisdom (hah) did not document this very useful function call.
  78.  
  79.  
  80. ; ----------------------------------------------------------------------------
  81. ;    ERRORLEVEL values that we return
  82.  
  83. ERR_None    equ    0        ; none!
  84. ERR_Usage    equ    1        ; problem on command line
  85. ERR_NotFound    equ    2        ; file not found
  86. ERR_BadFile    equ    3        ; given a device, or an unparsable
  87. ERR_CopyProblem    equ    4        ; problem while copying
  88. ERR_Renaming    equ    5        ; problem while renaming
  89.  
  90.  
  91. ; These are the data structures used in this program.  The first is used
  92. ; internally to describe the way a file name is broken down into its
  93. ; components.
  94.  
  95. FileDriveLen    equ    3
  96. FilePathLen    equ    65
  97. FileNameLen    equ    9
  98. FileExtLen    equ    5
  99.  
  100. FileNameS    struc
  101. drive    db    FileDriveLen dup(?)    ; drive        B:
  102. path    db        FilePathLen  dup(?)    ; path        \bin\booger
  103. fname    db    FileNameLen  dup(?)    ; filename    njmove
  104. fext    db    FileExtLen   dup(?)    ; extension    asm
  105. FileNameS    ends
  106.  
  107.  
  108. ; This data structure is the one that DOS uses to store the DTA.  When
  109. ; we call FINDFIRST and FINDNEXT, this information will be set up to contain
  110. ; the rest of the file that we are moving.
  111.  
  112. DOSDTAS    struc
  113. _dta_reserved    db    21 dup (?)    ; reserved space
  114. dta_attribute    db    ?        ; attribute byte
  115. dta_time    dw    ?        ; file modification time
  116. dta_date    dw    ?        ; file modification date
  117. dta_size    dd    ?        ; file size (doubleword!)
  118. dta_fname    db    13 dup (?)    ; file name (ASCIIZ)
  119. DOSDTAS    ends
  120.  
  121.  
  122. ; ============================================================================
  123.     .model    small
  124.     .stack    0100h        ; get a *real* stack
  125.  
  126.  
  127. ; ============================================================================
  128. ; Our data area follows.  This data area contains both initialized and
  129. ; uninitialized data.                            
  130.  
  131.     .data
  132.  
  133. Banner    db    "Nifty James' Famous Move Utility",cr,lf
  134.     db    "Version 1.00 of 16 January 1989",cr,lf
  135.     db    "(C)Copyright 1989 by Mike Blaszczak",cr,lf
  136.     db    lf,eos
  137.  
  138. NoSpace    db    "NJMOVE: Destination device ran out of space"
  139. CRLFSet    db    cr,lf,eos
  140.  
  141. NoSelf    db    "NJMOVE: A File can not be moved into itself"
  142.     db    cr,lf,eos
  143.  
  144. CantWrite    db    "NJMOVE: Couldn't write to destination device"
  145.     db    cr,lf,eos
  146.  
  147. CantRead    db    "NJMOVE: Couldn't read from source device"
  148.     db    cr,lf,eos
  149.  
  150. CantSDevice    db    "NJMOVE: Can't move from a device"
  151.     db    cr,lf,eos
  152.  
  153. CantDDevice    db    "NJMOVE: Can't move to a device"
  154.     db    cr,lf,eos
  155.  
  156. BadSource    db    "NJMOVE: Bad Source File Specification"
  157.     db    cr,lf,eos
  158.  
  159. BadDest    db    "NJMOVE: Bad Destination File Specification"
  160.     db    cr,lf,eos
  161.  
  162. CouldntRename    db    "NJMOVE: Could not perform rename operation"
  163.     db    cr,lf,eos
  164.  
  165. NameCollide    db    "NJMOVE: File naming collision; file already exists"
  166.     db    cr,lf,eos
  167.  
  168. CouldntDIR    db    "NJMOVE: Couldn't get default directory of drive "
  169.     db    eos
  170.  
  171. NoMatches    db    "NJMOVE: No files matched ",eos
  172.  
  173. UAborted    db    cr,lf,"NJMOVE: User Aborted",cr,lf,lf,eos
  174.  
  175. Usage    db    "Usage:",cr,lf
  176.     db    tab,"NJMOVE <source> <dest> [/C]",cr,lf
  177.     db    lf
  178.     db    tab,"<source>",tab,"source file name",cr,lf
  179.     db    tab,"<dest>",tab,tab,"destination file name",cr,lf
  180.     db    tab,tab," both file names may "
  181.     db    "contain wildcards",cr,lf
  182.     db    tab,"[/C]",tab,"specifies optional confirmation"
  183.     db    cr,lf,lf,eos
  184.  
  185. MovingMess    db    "Moving ",eos
  186. MoveCMess    db    "Move ",eos
  187.  
  188. CopyingMess    db    "Copying ",eos
  189. CopyCMess    db    "Copy ",eos
  190.  
  191. ToMess    db    " to ",eos
  192.  
  193. BackSlash    db    "\",eos
  194.  
  195. DotStar    db    "."
  196. Star    db    "*",eos
  197.  
  198. ConfirmPrompt    db    "? [No] ",eos
  199.  
  200.  
  201. confirmmode    db    0        ; set if the /C was to be found
  202. copymode    db    0        ; set to one if we must copy
  203.                 ;  from disk to disk instead of
  204.                 ;  just renaming the files
  205.     even
  206.  
  207. sourcespec    dw    0        ; source file specification
  208. destspec    dw    0        ; destination spec
  209.  
  210. sourcewildf    db    0        ; set if sourcef has wildcard name
  211. sourcewilde    db    0        ; set if sourcef has wildcard ext
  212. sourcehase    db    0        ; set if sourcef has extension
  213. sourcef    FileNameS <>        ; source description
  214. sourcefile    db    64 dup (0)    ; total file name
  215.  
  216. destwildf    db    0        ; set if destf has wildcard name
  217. destwilde    db    0        ; set if destf has wildcard ext
  218. desthase    db    0        ; set if destf has extension
  219. destf    FileNameS <>        ; destination description
  220. destfile    db    64 dup (0)    ; total file name
  221.  
  222. mydta    DOSDTAS <>        ; a place for DOS to store its stuff
  223.                 ; during FINDFIRST + FINDNEXT
  224.  
  225. ; These variables are used by the copyfile procedure
  226.  
  227. sourceh    dw    ?        ; handle for source file
  228. desth    dw    ?        ; destination file handle
  229.  
  230. ; ----------------------------------------------------------------------------
  231. ; These areas are all data buffers that are not initialized, generally.
  232.  
  233.     even
  234.  
  235. argvectors    dw    64 dup (0)    ; pointers to each argumnet
  236. argcount    dw    0        ; count of arguments
  237. argbuff    db    128 dup (0)    ; buffer to hold args for DS access
  238.  
  239. CopyBufferLen    equ    16384
  240. copybuffer    db    CopyBufferLen dup (?)
  241.                 ; this buffer is used while copying
  242.  
  243. temppath    db    FilePathLen dup(?)
  244.                 ; area used by check4path and
  245.                 ; normalizepath
  246.  
  247. fixedpath    db    FilePathLen dup(?)
  248.                 ; area used by normalize path, too.
  249.  
  250. ; This area is used by the confirm procedure
  251.  
  252. confirmreply    db    ?
  253.  
  254.  
  255. ; ============================================================================
  256. ; This is the program code.  The execution of the program starts at the
  257. ; very first instruction here.
  258.  
  259.     .code
  260.  
  261.     ; first, put the banner of our program on the screen
  262.  
  263.     mov    ax,@data
  264.     mov    ds,ax
  265.     mov    si,offset Banner
  266.     call    putstr
  267.  
  268.     ; set the DTA to point to our area
  269.  
  270.     mov    ah,SetDTA
  271.     mov    dx,offset mydta
  272.     int    INTDOS
  273.  
  274.     ; then, parse the command line
  275.  
  276.     call    parseline
  277.  
  278.     ; convert all of the arguments to upper case
  279.  
  280.     mov    ax,@data
  281.     mov    ds,ax
  282.  
  283.     mov    di,offset argvectors
  284.  
  285. arguploop:    mov    si,[di]        ; get the next arg
  286.     add    di,2
  287.  
  288.     and    si,si        ; is it 0000?
  289.     je    doneargup
  290.  
  291.     call    strupr
  292.     jmp    short arguploop
  293.  
  294. doneargup:
  295.     ; then, find the source file and dest file (maybe),
  296.     ; and check for options, too
  297.  
  298.  
  299.     mov    di,offset argvectors
  300. checkargsloop:
  301.     mov    si,[di]        ; get the next arg
  302.     add    di,2
  303.  
  304.     and    si,si        ; end of list?
  305.     je    donecheckargs
  306.  
  307.     mov    al,[si]        ; nope!     check it
  308.     cmp    al,'/'
  309.     je    gotoption
  310.     cmp    al,'-'
  311.     jne    notoption
  312.  
  313. gotoption:    inc    si        ; get the next character of option
  314.     mov    al,[si]
  315.     cmp    al,'C'        ; confirm?
  316.     jne    showusage    ; nope! error!
  317.  
  318.     mov    al,1
  319.     mov    confirmmode,al    ; confirmation mode
  320.     jmp    short checkargsloop
  321.  
  322. notoption:    mov    ax,sourcespec    ; do we have a source file?
  323.     and    ax,ax
  324.     jne    gotsource
  325.  
  326.     mov    sourcespec,si    ; nope!  this is our new source spec
  327.     jmp    short checkargsloop
  328.  
  329. gotsource:    mov    ax,destspec    ; how about a dest?
  330.     and    ax,ax
  331.     jne    showusage
  332.  
  333.     mov    destspec,si    ; nope!  this is the dest spec
  334.     jmp    short checkargsloop
  335.  
  336.     ; now, see if we have enough arugments
  337. donecheckargs:
  338.     mov    ax,argcount    ; did we get any args?
  339.     and    ax,ax
  340.     jne    noshowusage    ; if not, we must
  341.  
  342. showusage:
  343.     mov    si,offset Usage    ; display the usage
  344.     call    putstr
  345.     mov    ah,Terminate    ; and then
  346.     mov    al,ERR_Usage    ; terminate with error status
  347.     call    exit
  348.  
  349. noshowusage:
  350.     cmp    ax,1
  351.     ja    gotdest        ; was there a destination filespec?
  352.  
  353. nogotdest:
  354.     mov    si,offset Star    ; nope. asume dest filespec was
  355.     mov    di,offset destf.fname    ; *.*
  356.     call    strcpy
  357.     mov    si,offset DotStar
  358.     mov    di,offset destf.fext
  359.     call    strcpy
  360.     jmp    short destsetup
  361.  
  362. gotdest:    ; yes!  call splitfilename to handle
  363.     mov    si,destspec    ; the parse of the filename
  364.     mov    di,offset destf    ; into the destf area
  365.     call    splitfilename
  366.  
  367. destsetup:
  368.     ; before we goof with anything, see if the
  369.     ; destination has a file extension
  370.  
  371.     mov    si,offset destf.fext
  372.     call    strlen
  373.     and    cx,cx        ; something there?
  374.     je    destno
  375.  
  376.     mov    al,1
  377.     mov    desthase,al    ; yes! flag it!
  378.  
  379.  
  380. destno:    mov    si,offset destf.drive
  381.     call    strlen        ; was there a drive name given?
  382.     and    cx,cx
  383.     jne    gotdestdrive
  384.  
  385.     mov    ah,GetDefDisk        ; nope-get default from DOS
  386.     int    INTDOS
  387.     add    al,'A'            ; make it ASCII
  388.     mov    [si],al
  389.     inc    si            ; store it
  390.     mov    byte ptr [si],':'    ; add a colon
  391.     inc    si
  392.     mov    byte ptr [si],eos    ; and a zilcher
  393.  
  394. gotdestdrive:
  395.     mov    si,offset destf.path
  396.     call    strlen        ; was there a file path?
  397.     and    cx,cx
  398.     jne    gotdestpath
  399.  
  400.     ; put the default file path into the destf area
  401.     ; first, put a backslash in there
  402.  
  403.     mov    di,offset destf.path
  404.     mov    si,offset BackSlash
  405.     call    strcpy
  406.  
  407.     ; then ask DOS to put the default path into the
  408.     ; copybuffer area
  409.  
  410.     mov    si,offset copybuffer
  411.     mov    ah,GetDefDir
  412.     mov    dl,[destf.drive]    ; whats the drive?
  413.     sub    dl,('A'-1)
  414.     int    INTDOS
  415.     jnc    nodestdirproblem
  416.  
  417.     call    newline            ; print error message
  418.     mov    si,offset CouldntDIR
  419.     call    putstr
  420.     add    dl,('A'-1)        ; for that drive
  421.     mov    ah,PutChar
  422.     int    INTDOS
  423.     call    newline
  424.     mov    al,ERR_BadFile
  425.     call    exit
  426.     
  427. nodestdirproblem:    ; now, concatenate it together.  we will end up with
  428.     ; \path\place
  429.  
  430.     mov    si,offset copybuffer
  431.     mov    di,offset destf.path
  432.     call    strcat
  433.  
  434.     ; if the path is not just \, add a trailing \ to it
  435.     
  436.     mov    si,offset copybuffer
  437.     call    strlen
  438.     cmp    cx,1
  439.     jb    gotdestpath
  440.  
  441.     mov    di,offset destf.path
  442.     mov    si,offset BackSlash
  443.     call    strcat
  444.  
  445. gotdestpath:
  446.     ; now, we'll see if the filename is there
  447.  
  448.     mov    si,offset destf.fname
  449.     call    strlen
  450.     mov    bx,cx
  451.  
  452.     mov    si,offset destf.fext
  453.     call    strlen        ; now, cx = strlen(fext)
  454.                 ;   and bx = strlen(fname)
  455.  
  456.     and    cx,cx        ; is cx zero?
  457.     je    destnoproblem
  458.     
  459.     and    bx,bx
  460.     jne    destnoproblem
  461.  
  462.     ; we've found that there is no filename but there is an extension.
  463.     ; that's not too good.
  464.  
  465.     mov    si,offset BadDest
  466.     call    putstr
  467.     mov    al,ERR_BadFile
  468.     call    exit
  469.  
  470. destnoproblem:
  471.     and    bx,bx        ; does filename exist?
  472.     jne    desthasfn
  473.  
  474.     mov    si,offset Star    ; nope ... copy a star there
  475.     mov    di,offset destf.fname
  476.     call    strcpy
  477.  
  478. desthasfn:
  479.     and    cx,cx        ; okay, does the extension exist
  480.     jne    destfixed
  481.  
  482.     mov    si,offset DotStar; nope, copy ".*" into it
  483.     mov    di,offset destf.fext
  484.     call    strcpy
  485.  
  486. destfixed:
  487.     ; now, check to see if there's a wildcard in the filename
  488.     mov    si,offset destf.fname
  489.     mov    ah,'?'
  490.     call    strchr
  491.     and    ax,ax        ; is it?
  492.     jne    desthaswildf
  493.  
  494.     mov    si,offset destf.fname
  495.     mov    ah,'*'        ; how about a star?
  496.     call    strchr
  497.     and    ax,ax
  498.     je    destnowildf
  499.  
  500. desthaswildf:
  501.     mov    al,1
  502.     mov    destwildf,al    ; set the flag
  503.  
  504. destnowildf:
  505.     ; see if the destination extension has a wildcard
  506.     mov    si,offset destf.fext
  507.     mov    ah,'?'
  508.     call    strchr
  509.     and    ax,ax        ; is it?
  510.     jne    desthaswilde
  511.  
  512.     mov    si,offset destf.fext
  513.     mov    ah,'*'
  514.     call    strchr
  515.     and    ax,ax
  516.     je    destdone
  517.  
  518. desthaswilde:
  519.     mov    al,1
  520.     mov    destwilde,al
  521.  
  522. destdone:    ; normalize the path for the destf
  523.  
  524.     mov    si,offset destf
  525.     call    normpath
  526.  
  527.     ; does the filename have a wildcard?
  528.  
  529.     mov    al,destwildf
  530.     and    al,al
  531.     jne    destcompleted
  532.  
  533.     ; does it have an extension?
  534.  
  535.     mov    al,desthase
  536.     and    al,al
  537.     jne    destcompleted
  538.  
  539.     ; if not, check to see if the filename is a directory
  540.  
  541.     mov    si,offset destf
  542.     call    check4dir
  543.     and    ax,ax        ; did it work?
  544.     je    destcompleted
  545.  
  546.     mov    destwilde,al    ; yes! extension and filename
  547.     mov    destwildf,al    ; are wildcards now
  548.     
  549. destcompleted:    ; the destination file is all set up!
  550.     ; now, we can parse the source file name
  551.  
  552.     mov    si,sourcespec
  553.     mov    di,offset sourcef
  554.     call    splitfilename
  555.  
  556.     ; before we touch anything, see if we were given
  557.     ; a file extension
  558.  
  559.     mov    si,offset sourcef.fext
  560.     call    strlen
  561.     and    cx,cx        ; anything there?
  562.     je    sourceno
  563.  
  564.     mov    al,1
  565.     mov    sourcehase,al    ; yes! flag it
  566.  
  567.  
  568. sourceno:    ; check 'n' see if there is a drive there
  569.  
  570.     mov    si,offset sourcef.drive
  571.     call    strlen
  572.     and    cx,cx
  573.     jne    gotsourcedrive
  574.  
  575.     mov    ah,GetDefDisk        ; nope-get default from DOS
  576.     int    INTDOS
  577.     add    al,'A'
  578.     mov    [si],al
  579.     inc    si
  580.     mov    byte ptr [si],':'    ; and store a colon after it
  581.     inc    si
  582.     mov    byte ptr [si],eos    ; and a zilcher around it
  583.  
  584. gotsourcedrive:
  585.     mov    si,offset sourcef.path
  586.     call    strlen        ; was there a file path?
  587.     and    cx,cx
  588.     jne    gotsourcepath
  589.  
  590.     ; put the default file path into the sourcef area
  591.     ; first, put a backslash in there
  592.  
  593.     mov    si,offset BackSlash
  594.     mov    di,offset sourcef.path
  595.     call    strcpy
  596.  
  597.     ; then ask DOS to put the default path into the
  598.     ; copy buffer area
  599.  
  600.     mov    si,offset copybuffer
  601.     mov    ah,GetDefDir
  602.     mov    dl,[sourcef.drive]    ; whats the drive?
  603.     sub    dl,('A'-1)    ; make into A=1, B=2 ... for DOS
  604.     int    INTDOS
  605.     jnc    nosourcedirprob
  606.  
  607.     call    newline            ; print error message
  608.     mov    si,offset CouldntDIR
  609.     call    putstr
  610.     add    dl,('A'-1)        ; for that drive
  611.     mov    ah,PutChar
  612.     int    INTDOS
  613.     call    newline
  614.     mov    al,ERR_BadFile
  615.     call    exit
  616.  
  617. nosourcedirprob:    ; now, concatenate it togethre
  618.  
  619.     mov    si,offset copybuffer
  620.     mov    di,offset sourcef.path
  621.     call    strcat
  622.  
  623.     ; if the path is not just \, add a trailing \ to it
  624.     mov    si,offset copybuffer
  625.     call    strlen
  626.     cmp    cx,1
  627.     jb    gotsourcepath
  628.  
  629.     mov    di,offset sourcef.path
  630.     mov    si,offset BackSlash
  631.     call    strcat
  632.  
  633. gotsourcepath:
  634.     ; make sure there's a filename, even if its a wildcard.
  635.     ; check that it was a valid filename, too.
  636.  
  637.     mov    si,offset sourcef.fname
  638.     call    strlen
  639.     mov    bx,cx
  640.  
  641.     mov    si,offset sourcef.fext
  642.     call    strlen        ; now, cx = strlen(fext)
  643.                 ;    and bx = strlen(fname)
  644.  
  645.     and    cx,cx
  646.     je    sourcenoproblem
  647.  
  648.     and    bx,bx
  649.     jne    sourcenoproblem
  650.  
  651.     ; we've found that there is no filename but there is an extension
  652.     ; that's not good
  653.  
  654.     mov    si,offset BadSource
  655.     call    putstr
  656.     mov    al,ERR_BadFile
  657.     call    exit
  658.  
  659. sourcenoproblem:
  660.     and    bx,bx        ; does filename exist?
  661.     jne    sourcehasf
  662.  
  663.     mov    si,offset Star    ; nope ... copy a astar in there
  664.     mov    di,offset sourcef.fname
  665.     call    strcpy
  666.  
  667. sourcehasf:
  668.     and    cx,cx        ; does extension exist?
  669.     jne    sourcedone
  670.     mov    si,offset DotStar
  671.     mov    di,offset sourcef.fext
  672.     call    strcpy
  673.  
  674.     ; now, check to see if there's a wildcard in the filename
  675.     mov    si,offset sourcef.fname
  676.     mov    ah,'?'
  677.     call    strchr
  678.     and    ax,ax        ; is it?
  679.     jne    sourcehaswildf
  680.  
  681.     mov    si,offset sourcef.fname
  682.     mov    ah,'*'        ; how about a star?
  683.     call    strchr
  684.     and    ax,ax
  685.     je    sourcenowildf
  686.  
  687. sourcehaswildf:
  688.     mov    al,1
  689.     mov    sourcewildf,al    ; set the flag
  690.  
  691. sourcenowildf:
  692.     ; see if the sourceination extension has a wildcard
  693.     mov    si,offset sourcef.fext
  694.     mov    ah,'?'
  695.     call    strchr
  696.     and    ax,ax        ; is it?
  697.     jne    sourcehaswilde
  698.  
  699.     mov    si,offset sourcef.fext
  700.     mov    ah,'*'
  701.     call    strchr
  702.     and    ax,ax
  703.     je    sourcedone
  704.  
  705. sourcehaswilde:
  706.     mov    al,1
  707.     mov    sourcewilde,al
  708.  
  709. sourcedone:    ; last, normalize the source file path
  710.  
  711.     mov    si,offset sourcef
  712.     call    normpath
  713.  
  714.     mov    al,sourcewildf
  715.     and    al,al
  716.     jne    sourcecompleted
  717.  
  718.     mov    al,sourcehase
  719.     and    al,al
  720.     jne    sourcecompleted
  721.  
  722.     mov    si,offset sourcef
  723.     call    check4dir
  724.     and    ax,ax        ; did it work?
  725.     je    sourcecompleted
  726.  
  727.     mov    sourcewildf,al    ; yes! filename and
  728.     mov    sourcewilde,al    ; extension are now wildcards!
  729.  
  730.  
  731. sourcecompleted:    ; filenames are all set!
  732.     ; okay, almost there!  check to see if the drives
  733.     ; are the same.  if they are, we're renaming.  if
  734.     ; they're not, we're copying and deleting.
  735.  
  736.     mov    si,offset sourcef.drive
  737.     mov    al,[si]
  738.     mov    si,offset destf.drive
  739.     cmp    al,[si]
  740.     je    workhorse
  741.  
  742.     mov    al,1        ; set copying flag
  743.     mov    copymode,al
  744.  
  745.     ; right! everything is ready.  we'll make the source
  746.     ; file name into something meaningful
  747.  
  748. workhorse:    mov    si,offset sourcef
  749.     mov    di,offset sourcefile
  750.     call    makefilename
  751.  
  752.     ; just for kicks, we'll try to open that file.
  753.  
  754.     mov    ah,OpenHandle
  755.     mov    al,0
  756.     mov    dx,offset sourcefile
  757.     int    INTDOS
  758.  
  759.     jc    dothefindfirst
  760.  
  761.     mov    bx,ax
  762.     mov    ah,IOCTL
  763.     mov    al,0        ; see if it is a device
  764.     int    INTDOS
  765.  
  766.     and    dx,0080h    ; check ISDEV bit
  767.     je    notdevice
  768.  
  769. deviceblowout:    mov    si,offset CantSDevice
  770.     call    putstr
  771.     mov    ax,ERR_BadFile
  772.     call    exit
  773.  
  774.  
  775. notdevice:    mov    ah,CloseHandle
  776.     int    INTDOS
  777.  
  778.  
  779.     ; we'll try to find the first matching file
  780.  
  781. dothefindfirst:    mov    ah,FindFirst
  782.     mov    dx,offset sourcefile
  783.     xor    cx,cx
  784.     int    INTDOS
  785.  
  786.     jnc    foundone
  787.  
  788. notfound:    mov    si,offset NoMatches
  789.     call    putstr
  790.     mov    si,offset sourcefile
  791.     call    putstr
  792.     call    newline
  793.     mov    ax,ERR_NotFound
  794.     call    exit
  795.  
  796.     ; this loop is the main thing.  we've called findfirst,
  797.     ; and the dta block is set up with the file we found.
  798.     ; we'll figure out the destination file name, and
  799.     ; then move or copy the file.
  800.  
  801.     ; at the bottom of the loop, we'll call findnext and
  802.     ; see if there are more matching files.  if its there,
  803.     ; we can loop back here and process that next file.
  804.  
  805. foundone:    mov    si,offset mydta
  806.     mov    di,offset sourcef
  807.     call    popfound
  808.     mov    si,offset sourcef
  809.     mov    di,offset sourcefile
  810.     call    makefilename
  811.  
  812.     ; if the source filename has a wildcard *and* the
  813.     ; dest filename has a wild card, copy source filename
  814.     ; into the destination filename
  815.  
  816.     mov    al,destwildf
  817.     and    al,al
  818.     je    notwildf
  819.  
  820.     mov    si,offset sourcef.fname
  821.     mov    di,offset destf.fname
  822.     call    strcpy
  823.  
  824.  
  825. notwildf:    ; if the source file extension has a wildcard,    and
  826.     ; the destination extension has a wildcard, copy
  827.     ; the source extension into the destination extension
  828.  
  829.     mov    al,destwilde
  830.     and    al,al
  831.     je    notwilde
  832.  
  833.     mov    si,offset sourcef.fext
  834.     mov    di,offset destf.fext
  835.     call    strcpy
  836.  
  837.  
  838. notwilde:    ; create the destination file into an ASCIIZ string
  839.  
  840.     mov    si,offset destf
  841.     mov    di,offset destfile
  842.     call    makefilename
  843.  
  844.     ; print a message for what we are going to do
  845.  
  846.     mov    al,copymode
  847.     mov    ah,confirmmode
  848.  
  849.     and    al,al
  850.     je    wearemoving
  851.  
  852.     mov    si,offset CopyingMess    ; we're copying
  853.  
  854.     and    ah,ah
  855.     je    printsource        ; without confirmation
  856.  
  857.     mov    si,offset CopyCMess
  858.     jne    printsource
  859.  
  860. wearemoving:    mov    si,offset MovingMess    ; we're moving
  861.     and    ah,ah
  862.     je    printsource        ; without confirmation?
  863.  
  864.     mov    si,offset MoveCMess
  865.  
  866.  
  867. printsource:    call    putstr
  868.  
  869.     ; either
  870.     ;   "Moving sourcefile to destfile"
  871.     ; or
  872.     ;   "Copying sourcefile to destfile"
  873.  
  874.     mov    si,offset sourcefile
  875.     call    putstr
  876.  
  877. whereto:    ; print " to "
  878.  
  879.     mov    si,offset ToMess
  880.     call    putstr
  881.  
  882.     mov    si,offset destfile
  883.     call    putstr
  884.  
  885.  
  886.     ; before we actually do it, call confirmation, if need be
  887.  
  888.     and    ah,ah
  889.     jne    askfirst    ; no confirmation
  890.  
  891.     call    newline
  892.     jmp    short goahead
  893.  
  894. askfirst:    call    getyesno    ; do confirmation
  895.     call    newline
  896.  
  897.     cmp    ax,0        ; no?
  898.     je    loopbottom    ; skip it, then
  899.  
  900.     cmp    ax,1        ; yes?
  901.     je    goahead        ; go right ahead
  902.  
  903.     xor    ax,ax        ; all of them!
  904.     mov    confirmmode,al    ; (forget about confirming)
  905.  
  906.     ; actually do it
  907.  
  908. goahead:    mov    al,copymode
  909.     and    al,al
  910.     jne    doacopy
  911.  
  912. doamove:
  913.     mov    dx,offset sourcefile
  914.     mov    di,offset destfile
  915.     mov    ah,RenameFile
  916.     int    INTDOS
  917.     jnc    loopbottom
  918.  
  919.     ; decide which error happened.  AX=0005 is bad name,
  920.     ; anything else is different.
  921.  
  922.     mov    bx,ax
  923.     mov    al,ERR_Renaming
  924.     
  925.     mov    si,offset NameCollide
  926.     cmp    bx,5
  927.     je    renamingcollision
  928.  
  929.     mov    si,offset CouldntRename
  930.  
  931. renamingcollision:    call    newline
  932.     call    putstr
  933.     mov    ah,ERR_Renaming
  934.     call    exit
  935.  
  936. doacopy:    call    copyfile
  937.  
  938. loopbottom:    mov    ah,FindNext
  939.     int    INTDOS
  940.     jc    cleanexit    ; relative jnc out of range
  941.     jmp    foundone
  942.  
  943.  
  944.     
  945.     
  946. cleanexit:
  947.     mov    al,ERR_None    ; return with no error
  948.     call    exit
  949.  
  950.  
  951.  
  952. ; ============================================================================
  953. ; These subroutines are used by the programs' main routine
  954.  
  955.  
  956. ; ----------------------------------------------------------------------------
  957. ; exit
  958. ; On entry, AL contains the error level that the program should return.
  959. ;
  960. ; This routine will terminate to DOS.  It does no housekeeping functions.
  961.  
  962. exit    proc    near
  963.  
  964.     mov    ah,Terminate    ; make a Terminate call
  965.     int    INTDOS
  966.  
  967. exit    endp
  968.  
  969.  
  970. ; ----------------------------------------------------------------------------
  971. ; strlen
  972. ; On entry, DS:SI points to a string.
  973. ;
  974. ; On return, CX will contain the length of the string.
  975.  
  976. strlen    proc    near
  977.  
  978.     push    ax
  979.     push    si
  980.  
  981.     xor    cx,cx        ; zero the length count
  982.  
  983. sl_c:    mov    al,[si]        ; get a character
  984.     inc    si
  985.     inc    cx        ; increment pointer and count
  986.     and    al,al        ; is it end of string?
  987.     jne    sl_c        ;  loop back if it isn't
  988.  
  989.     dec    cx        ; adjust the count down
  990.  
  991.     pop    si
  992.     pop    ax
  993.     ret
  994.  
  995. strlen    endp
  996.  
  997.  
  998.  
  999. ; ----------------------------------------------------------------------------
  1000. ; newline
  1001. ; This routine prints a carriage return and a linefeed.  We will destroy
  1002. ; no registers.
  1003.  
  1004.  
  1005. newline    proc    near
  1006.  
  1007.     push    si
  1008.     push    ds        ; save the DS:SI
  1009.     push    ax
  1010.  
  1011.     mov    ax,@data
  1012.     mov    ds,ax
  1013.     mov    si,offset CRLFSet
  1014.     call    putstr
  1015.  
  1016.     pop    ax        ; restore the registers
  1017.     pop    ds
  1018.     pop    si
  1019.     ret
  1020.  
  1021. newline    endp
  1022.  
  1023.  
  1024. ; ----------------------------------------------------------------------------
  1025. ; putstr
  1026. ; On entry, DS:SI points to a string.
  1027. ;
  1028. ; This routine prints the passed string to STDOUT, and does nothing else.
  1029. ; The string is not altered in any way.
  1030.  
  1031. putstr    proc    near
  1032.  
  1033.     push    dx
  1034.     push    cx
  1035.     push    bx
  1036.     push    ax        ; save the regs we use
  1037.     push    si
  1038.  
  1039.     xor    cx,cx        ; zero the length count
  1040.  
  1041. ps_l:    mov    al,[si]        ; get a character
  1042.     inc    si
  1043.     inc    cx        ; increment pointer and count
  1044.     and    al,al        ; is it end of string?
  1045.     jne    ps_l        ;  loop back if it isn't
  1046.  
  1047.     dec    cx        ; adjust the count down
  1048.  
  1049.     mov    ah,WriteHandle    ; ask DOS to write to the stdout
  1050.     mov    bx,STDOUT
  1051.     
  1052.     pop    si        ; restore the SI
  1053.     mov    dx,si        ; and put it into dx for DOS
  1054.     int    INTDOS
  1055.  
  1056.     pop    ax
  1057.     pop    bx
  1058.     pop    cx
  1059.     pop    dx
  1060.     ret
  1061.  
  1062. putstr    endp
  1063.  
  1064.  
  1065. ; ----------------------------------------------------------------------------
  1066. ; strcat
  1067. ; On entry, DS:SI points to the concatenant, and ES:DI points to the
  1068. ; concatenand:
  1069. ;
  1070. ;    DS:SI -> "Have Antlers"
  1071. ;    ES:DI -> "Mooses "
  1072. ;    call    strcat
  1073. ;    ES:DI -> "Mooses Have Antlers"
  1074.  
  1075.  
  1076. strcat    proc    near
  1077.  
  1078.     push    ax
  1079.     push    si        ; save the registers
  1080.     push    di
  1081.  
  1082.     ; first, find the end
  1083.  
  1084. sc_next:    mov    al,es:[di]    ; get the char
  1085.     inc    di
  1086.     and    al,al        ; is it zilcher?
  1087.     jne    sc_next        ; nope ... keep looping
  1088.  
  1089.     dec    di        ; point to the zilcher
  1090.  
  1091.     ; now, we can just call strcpy to move the concatenant
  1092.     ; over the end of the concatenand.
  1093.  
  1094.     call    strcpy
  1095.  
  1096.     pop    di
  1097.     pop    si
  1098.     pop    ax
  1099.  
  1100.     ret
  1101. strcat    endp
  1102.  
  1103.  
  1104. ; ----------------------------------------------------------------------------
  1105. ; strcpy
  1106. ; On entry, DS:SI points to a string and ES:DI points to storage space.
  1107. ;
  1108. ; This routine will copy the string from DS:SI to ES:DI.
  1109.  
  1110. strcpy    proc    near
  1111.  
  1112.     push    di        ; save the registers we use
  1113.     push    si
  1114.     push    ax
  1115.  
  1116. sc_nextc:    mov    al,[si]        ; get another character
  1117.     inc    si
  1118.     mov    es:[di],al    ; store it
  1119.     inc    di
  1120.  
  1121.     and    al,al        ; if that was the 0 character, quit
  1122.     jne    sc_nextc    ; else keep working on the string
  1123.  
  1124.     pop    ax        ; restore the registers we used
  1125.     pop    si
  1126.     pop    di
  1127.     ret
  1128. strcpy    endp
  1129.  
  1130.  
  1131. ; ----------------------------------------------------------------------------
  1132. ; strchr
  1133. ; On entry, DS:SI points to a string, and ah contains a character to match.
  1134. ; The routine will work forward through the string until it finds the end
  1135. ; of the string or matches the character.  On return, AX will be 0000 if
  1136. ; there was no match, or DS:AX will point to the character in the string
  1137. ; if the character wasn't found.
  1138.  
  1139. strchr    proc    near
  1140.  
  1141.     push    si        ; save si
  1142.  
  1143. strchrnc:    mov    al,[si]        ; get a character
  1144.     inc    si        ; and increment pointer
  1145.     cmp    al,ah        ; is it a match?
  1146.     je    strchrhit
  1147.  
  1148.     and    al,al        ; nope.  was it '\0'?
  1149.     jne    strchrnc    ; if not, go do more
  1150.  
  1151.     xor    ax,ax        ; yes, the string ended
  1152.     je    strchrexit    ; so make AX null for failure
  1153.  
  1154. strchrhit:    mov    ax,si        ; got the character!
  1155.     dec    ax        ; put pointer into ax
  1156.  
  1157. strchrexit:    pop    si        ; restore si
  1158.     ret            ; and go home
  1159.  
  1160. strchr    endp
  1161.  
  1162.  
  1163. ; ----------------------------------------------------------------------------
  1164. ; strrchr
  1165. ; On entry, DS:SI points to a string, and ah contains a character to match.
  1166. ; The routine will step backward, starting at the end of the string, and
  1167. ; work until it finds a match.  If the program makes it through the whole
  1168. ; string without finding the character, it will return with AX set to 0000.
  1169. ; Otherwise, DS:AX points the the last occurrence of the character in the
  1170. ; string.
  1171.  
  1172.  
  1173. strrchr    proc    near
  1174.  
  1175.     push    cx        ; save the registers we use
  1176.     push    si
  1177.     xor    cx,cx        ; zero cx since we use it to count
  1178.  
  1179. strrchrfe:    mov    al,[si]        ; get the next character
  1180.     inc    si        ; increment the pointer
  1181.     inc    cx        ; increment the count of chars
  1182.     and    al,al        ; is this the end of the string?
  1183.     jne    strrchrfe
  1184.  
  1185.     cmp    cx,0        ; is the string empty?
  1186.     je    strrchrfail    ; if so, automatic failure
  1187.  
  1188. strrchrloop:    dec    si        ; point to the null
  1189.     mov    al,[si]        ; is that the character?
  1190.     cmp    ah,al
  1191.     je    strrchrhit    ; if so, we've hit!
  1192.     dec    cx
  1193.     jne    strrchrloop    ; if not, loop more
  1194.  
  1195. strrchrfail:    xor    ax,ax
  1196.     je    strrchrexit    ; return AX as 0000
  1197.  
  1198. strrchrhit:    mov    ax,si        ; got the character! move pointer
  1199.                 ; into ax
  1200.  
  1201. strrchrexit:    pop    si
  1202.     pop    cx
  1203.     ret
  1204.  
  1205. strrchr    endp
  1206.  
  1207.  
  1208. ; ----------------------------------------------------------------------------
  1209. ; strupr
  1210. ; On entry, DS:SI points to a string and ES:DI points to storage space.
  1211. ;
  1212. ; This routine will convert the entire content of the string to uppercase.
  1213. ; It won't alter any registers, nor will it change non-alphabetic characters
  1214. ; in the str.
  1215.  
  1216.  
  1217. strupr    proc    near
  1218.  
  1219.     cld
  1220.     push    ax
  1221.     push    si
  1222.  
  1223. struprloop:    lodsb            ; get the next character
  1224.     and    al,al        ; is it zilcho?
  1225.     je    struprdone
  1226.  
  1227.     cmp    al,'a'        ; is it less than 'a'?
  1228.     jl    struprloop    ; yep ... keep looping
  1229.  
  1230.     cmp    al,'z'        ; is it bigger than 'z'?
  1231.     jg    struprloop    ; yes; don't modify it
  1232.  
  1233.     sub    al,('a' - 'A')
  1234.     mov    [si-1],al    ; restore that character
  1235.     jmp    short struprloop
  1236.  
  1237. struprdone:    pop    si
  1238.     pop    ax
  1239.     ret
  1240.  
  1241. strupr    endp
  1242.  
  1243.  
  1244. ; ----------------------------------------------------------------------------
  1245. ; parseline
  1246. ; This routine tries to parse the command line.  It sets up the variables
  1247. ; in the data segment (argcount and arglist), as well as copying the
  1248. ; information into a local area in the data segment.  The routine will
  1249. ; not preserve any of the registers it uses.
  1250.  
  1251. parseline    proc    near
  1252.  
  1253.     mov    ah,GetPSP    ; get the PSP from DOS
  1254.     int    INTDOS
  1255.  
  1256.     mov    ds,bx        ; move the PSP into ds
  1257.     mov    si,0081h    ; address the command line
  1258.  
  1259.     xor    dx,dx
  1260.     mov    dl,[si-1]    ; get the length of the line
  1261.     add    dl,081h
  1262.  
  1263.     mov    ax,@data    ; now, set up ES to point to our
  1264.     mov    es,ax        ; own data areas
  1265.     mov    di,offset argbuff
  1266.  
  1267.  
  1268. nextarg:
  1269. eatwhite:
  1270.     mov    al,[si]
  1271.     inc    si        ; get the next character
  1272.  
  1273.     cmp    al,space    ; is it a space?
  1274.     je    eatwhite
  1275.     cmp    al,tab        ; or a tab?
  1276.     je    eatwhite    ; yes ... skip over it
  1277.  
  1278.     dec    si        ; point si at the first non-white
  1279.     push    si        ; and save a copy of it for later
  1280.  
  1281. eatarg:    mov    al,[si]        ; get the last character
  1282.     inc    si
  1283.     cmp    al,space    ; is it a whitespace?
  1284.     jle    gotarg
  1285.     jmp    short eatarg    ; nope... keep skipping
  1286.  
  1287. gotarg:
  1288.     dec    si
  1289.     mov    byte ptr [si],0    ; make the argument a string
  1290.  
  1291.     pop    ax        ; get the pointer to this arg
  1292.  
  1293.     xchg    ax,si        ; put si into DS:SI
  1294.  
  1295.     call    strcpy        ; store in our data segment
  1296.  
  1297.     call    strlen        ; get the length of the string
  1298.     push    cx        ; save it
  1299.  
  1300.     xchg    si,ax
  1301.  
  1302.     and    cx,cx        ; if this string has no length,
  1303.     je    added        ;  don't add it to the table
  1304.  
  1305.     ; here, DS:AX points to the end of the string and
  1306.     ; ES:DI points to the place where the string was stored.
  1307.     ; DS:SI still points to the beginning of the string.
  1308.  
  1309. add2table:    mov    ax,es:argcount
  1310.     shl    ax,1        ; get offset into array
  1311.     add    ax,offset argvectors
  1312.     xchg    ax,si
  1313.     mov    es:[si],di    ; store the offset of this arg
  1314.     xchg    si,ax
  1315.  
  1316.     inc    es:argcount    ; increment the count of args
  1317.  
  1318. added:
  1319.     ; adjust ES:DI
  1320.  
  1321.     pop    cx
  1322.     add    di,cx        ; get the length and fix it in
  1323.     inc    di        ; make DS:SI point to the next
  1324.     inc    si        ; free spot
  1325.  
  1326.     ; see if we are done
  1327.  
  1328.     cmp    dx,si
  1329.     ja    nextarg
  1330.  
  1331. wearedone:
  1332.     ret
  1333. parseline    endp
  1334.  
  1335.  
  1336. ; ----------------------------------------------------------------------------
  1337. ; splitfilename
  1338. ; this procedure accepts ds:si as a pointer to a full file name.  it also
  1339. ; expects that es:di points to a FileNameS.  The routine will copy
  1340. ; information from the full file name into the FileNameS structure.
  1341.  
  1342. splitfilename    proc    near
  1343.  
  1344.     push    cx
  1345.     push    si        ; save the regs we use
  1346.     push    di
  1347.  
  1348.     ; to get started, check to see if there's a drivespec
  1349.  
  1350.     mov    al,[si+1]    ; is there a colon for the
  1351.     cmp    al,':'        ; second character?
  1352.     jne    sfn_nodrive    ; nope! don't worry about it
  1353.  
  1354. sfn_gotdrive:    mov    es:[di+1].drive,al    ; store the colon
  1355.     mov    al,[si]            ; get the drive letter
  1356.     mov    es:[di].drive,al
  1357.     add    di,2        ; make es:di point after it
  1358.     add    si,2        ; and make si point after drive
  1359.  
  1360.  
  1361. sfn_nodrive:    ; since there's no drive spec, just zero the drive.
  1362.  
  1363.     xor    ax,ax
  1364.     mov    es:[di].drive,al ; stick it
  1365.  
  1366.  
  1367. sfn_drivedone:    ; now that drive is done, point es:di to the path
  1368.  
  1369.     pop    di        ; point to the path area
  1370.     push    di
  1371.     add    di,path
  1372.  
  1373.     ; see if there was a path in the filename we got
  1374.  
  1375.     mov    al,[si]        ; get the first char
  1376.     cmp    al,'.'
  1377.     jne    sfn_notrelpath
  1378.     je    sfn_hasrelpath
  1379.  
  1380.  
  1381. sfn_notrelpath:    mov    ah,'\'        ; the path character
  1382.     call    strchr
  1383.  
  1384.     and    ax,ax        ; is AX == 0000?
  1385.     jne    sfn_haspath
  1386.  
  1387. sfn_hasrelpath:    mov    ax,si        ; yes -- point to the start of
  1388.                 ; the string
  1389.  
  1390. sfn_haspath:    ; there's a path!  now, try to find the *last*
  1391.     ; character of the path.
  1392.  
  1393.     mov    dx,ax        ; save AX for now
  1394.     mov    ah,'\'
  1395.     call    strrchr        ; find the last '\'
  1396.     xchg    ax,dx
  1397.  
  1398.     and    dx,dx
  1399.     jne    sfn_hasend
  1400.  
  1401.     ; hmph!  there is no '\', either way you look
  1402.     ; does the path begin with '.'?
  1403.  
  1404.     cmp    byte ptr [si],'.'
  1405.     jne    sfn_nopath    ; nope!  no path name
  1406.                 ; yes!  relative path name!
  1407.  
  1408.  
  1409. sfn_relpath:    call    strlen        ; if no '\', point to end of
  1410.     mov    dx,si        ;   string
  1411.     add    dx,cx
  1412.  
  1413.     cmp    ax,dx        ; is our end and start equal?
  1414.     je    sfn_nopath    ; no path here, mon!
  1415.  
  1416.     ; now, copy the string from DS:AX to DS:DX into ES:DI.
  1417.  
  1418. sfn_hasend:    mov    si,ax        ; start with ax
  1419.     mov    cx,FilePathLen-2
  1420.  
  1421. sfn_movepath:    mov    al,[si]
  1422.     inc    si        ; get this character
  1423.     mov    es:[di],al    ; and store it in path
  1424.     inc    di
  1425.  
  1426.     dec    cx        ; decrement max char count.
  1427.     jne    sfn_movingpath    ; if too many, truncate
  1428.  
  1429.     mov    al,'\'
  1430.     mov    es:[di],al    ; truncate with a backslash
  1431.     inc    di
  1432.     mov    si,dx        ; update the pointer so's it
  1433.     inc    si        ; points to the filename now
  1434.     jmp    short sfn_movedpath
  1435.  
  1436. sfn_movingpath:
  1437.     cmp    dx,si        ; is si>dx?  (work to dx)
  1438.     jae    sfn_movepath    ; no ... keep looping
  1439.  
  1440. sfn_movedpath:
  1441. sfn_nopath:    xor    ax,ax        ; set up the ax so we can
  1442.     mov    es:[di],al    ; store a null in .path area
  1443.  
  1444.     ; now, we have the path done.  mess with moving
  1445.     ; the file name and the extension. DS:SI already
  1446.     ; points to the file name.
  1447.  
  1448. sfn_pathdone:    pop    di        ; make es:di point to filename
  1449.     push    di
  1450.     add    di,fname
  1451.  
  1452.     mov    cx,FileNameLen
  1453.  
  1454. sfn_movefn:    mov    al,[si]        ; get the next character
  1455.     cmp    al,'.'        ; end of filename?
  1456.     je    sfn_gotext
  1457.  
  1458.     dec    cx        ; too many characters?
  1459.     jne    sfn_moveingfn    ; yep!  truncate.
  1460.  
  1461.     mov    al,0
  1462.     mov    es:[di],al    ; terminate the filename
  1463. sfn_blowoff:    mov    al,[si]        ; skip ahead until we find eos or
  1464.     cmp    al,'.'        ; a period
  1465.     je    sfn_gotext
  1466.     inc    si
  1467.     and    al,al        ; is it a zilcher?
  1468.     jne    sfn_blowoff
  1469.     je    sfn_movedfn
  1470.  
  1471. sfn_moveingfn:
  1472.     mov    es:[di],al    ; store this character
  1473.     inc    si
  1474.     inc    di
  1475.     and    al,al        ; did we just write an eos?
  1476.     jne    sfn_movefn
  1477.  
  1478. sfn_movedfn:    xor    ax,ax
  1479.     pop    di        ; make addressing to strucutre
  1480.     push    di
  1481.     mov    es:[di].fext,al
  1482.     jmp    short sfn_exit    ; and split this joint
  1483.  
  1484. sfn_gotext:    mov    ah,0        ; write an end-of-string for
  1485.     mov    es:[di],ah    ; the filename, first
  1486.  
  1487.     mov    cx,FileExtLen
  1488.  
  1489.     pop    di
  1490.     push    di
  1491.     add    di,fext        ; make es:di point to filename
  1492.  
  1493. sfn_moveext:    mov    es:[di],al    ; store the character
  1494.     inc    si
  1495.     inc    di        ; increment the pointers
  1496.     and    al,al        ; did we just write a '\0'?
  1497.     je    sfn_exit
  1498.  
  1499.     cmp    cx,0        ; is it zero yet?
  1500.     jne    snf_movingext
  1501.  
  1502.     mov    al,0
  1503.     dec    di
  1504.     mov    es:[di],al
  1505.     jmp    short sfn_exit
  1506.  
  1507. snf_movingext:    dec    cx
  1508.     mov    al,[si]        ; get next character
  1509.     jmp    short sfn_moveext
  1510.  
  1511. sfn_exit:
  1512.     ; on the way out, we will check to see if the path
  1513.     ; name was a relative file.  if so, we will make
  1514.     ; sure a slash is appended to it.
  1515.  
  1516.     pop    di
  1517.     push    di
  1518.     add    di,path
  1519.  
  1520.     mov    al,[di]
  1521.     cmp    al,'.'        ; not a relative, no problem
  1522.     jne    sfn_nofix
  1523.  
  1524.     ; fix relative path
  1525.  
  1526.     mov    si,di
  1527.     call    strlen
  1528.     add    si,cx
  1529.     dec    si        ; point to last character
  1530.     mov    al,[si]        ; get that character
  1531.     cmp    al,'\'        ; slash?
  1532.     je    sfn_nofix    ;   no problem
  1533.  
  1534.     inc    si        ; make it a slash
  1535.     mov    byte ptr [si],'\'
  1536.     inc    si
  1537.     mov    byte ptr [si],eos
  1538.  
  1539. sfn_nofix:    ; restore the regs we saved
  1540.     pop    di
  1541.     pop    si
  1542.     pop    cx
  1543.     ret
  1544. splitfilename    endp
  1545.  
  1546.  
  1547. ; ----------------------------------------------------------------------------
  1548. ; makefilename
  1549. ; On entry, DS:SI points to a FileNameS, and ES:DI points to a storage
  1550. ; buffer for a file name.  The routine will string all of the elements
  1551. ; of the strucutre together to make a real full file name.
  1552.  
  1553. makefilename    proc    near
  1554.  
  1555.     push    ax
  1556.     push    si
  1557.     push    di
  1558.     mov    ax,si        ; put base pointer into ax
  1559.  
  1560.     ; first copy the drive specifier
  1561.  
  1562.     mov    si,ax
  1563.     add    si,drive    ; copy the drive
  1564.     call    strcpy
  1565.  
  1566.     ; then concatenate the path
  1567.  
  1568.     mov    si,ax
  1569.     add    si,path
  1570.     call    strcat
  1571.  
  1572.     ; then concatenate the filename
  1573.  
  1574.     mov    si,ax
  1575.     add    si,fname
  1576.     call    strcat
  1577.     
  1578.     ; now concatenate the extension
  1579.  
  1580.     mov    si,ax
  1581.     add    si,fext
  1582.     call    strcat
  1583.  
  1584.     ; that's all!
  1585.  
  1586.     pop    di
  1587.     pop    si    ; restore the registers and return home
  1588.     pop    ax
  1589.     ret
  1590. makefilename    endp
  1591.  
  1592.  
  1593. ; ----------------------------------------------------------------------------
  1594. ; popfound
  1595. ; This routine accepts a pointer to a DOSDTAS in DS:DI, and a pointer to
  1596. ; a FileNameS in ES:DI.  It takes the filename and extension fields from
  1597. ; the DOSDTAS and puts them into the fname and fext portions of the
  1598. ; FileNameS structure.
  1599.  
  1600. popfound    proc
  1601.  
  1602.     push    ax
  1603.     push    cx
  1604.     push    dx
  1605.     push    si    ; save the regs we use
  1606.     push    di
  1607.     
  1608.     mov    dx,si        ; and make a copy of the parms
  1609.     mov    cx,di        ; to easily address the structs
  1610.  
  1611.     ; first, work on moving the filename
  1612.  
  1613.     mov    si,dx
  1614.     add    si,dta_fname
  1615.  
  1616.     mov    di,cx
  1617.     add    di,fname
  1618.  
  1619. popfoundf:    mov    al,[si]        ; get this char
  1620.     cmp    al,'.'        ; period yet?
  1621.     je    popfounde
  1622.     mov    es:[di],al    ; store it
  1623.  
  1624.     and    al,al        ; is it a zilcher?    
  1625.     jne    popfoundfing
  1626.  
  1627.     mov    di,cx        ; yep!  zilch out extension too
  1628.     add    di,fext
  1629.     mov    es:[di],al
  1630.     jmp    short popfounddone
  1631.  
  1632.  
  1633. popfoundfing:    inc    di
  1634.     inc    si
  1635.     jmp    short popfoundf    ; loop for more
  1636.  
  1637. popfounde:    mov    ah,0
  1638.     mov    es:[di],ah    ; terminate the filename with 0
  1639.     mov    di,cx
  1640.  
  1641.     add    di,fext        ; point to the extension
  1642.  
  1643. popfoundw:    mov    es:[di],al
  1644.     inc    di
  1645.     inc    si
  1646.     and    al,al        ; is it zero?
  1647.     je    popfounddone    ; yes, quit!
  1648.     mov    al,[si]        ;(get next char)
  1649.     jne    popfoundw    ; nope ... keep working
  1650.  
  1651. popfounddone:
  1652.     pop    di
  1653.     pop    si        ; restore registers
  1654.     pop    dx
  1655.     pop    cx
  1656.     pop    ax
  1657.     ret
  1658. popfound    endp
  1659.  
  1660.  
  1661. ; ----------------------------------------------------------------------------
  1662. ; copyfile
  1663. ; this procedure completely copies a file.  it copies the file in
  1664. ; sourcefile to the file in destfile.  If there is an error during
  1665. ; the copy, the destfile is automatically deleted and the program
  1666. ; terminates via exit.  If the copy is successful, the source file
  1667. ; is deleted.  The dest file will inherit the same file time and
  1668. ; date stamp as the source file.
  1669.  
  1670. copyfile    proc    near
  1671.  
  1672.     ; first, try to open the dest file for reading
  1673.  
  1674.     mov    ah,OpenHandle        ; open the
  1675.     mov    dx,offset destfile    ; dest for
  1676.     mov    al,0            ; reading
  1677.     int    INTDOS
  1678.     jc    cf_nameokay
  1679.  
  1680.     ; we could read the source file!  the file already
  1681.     ; exists, so we must abort.
  1682.  
  1683.     mov    bx,ax
  1684.     mov    ah,CloseHandle        ; close the handle and
  1685.     call    newline
  1686.     mov    si,offset NameCollide    ; print an error!
  1687.     call    putstr
  1688.     mov    ah,ERR_CopyProblem
  1689.     call    exit
  1690.  
  1691. cf_nameokay:    ; try to open the source file
  1692.  
  1693.     mov    ah,OpenHandle        ; open the 
  1694.     mov    dx,offset sourcefile    ; source for
  1695.     mov    al,0            ; for reading
  1696.     int    INTDOS
  1697.  
  1698.     ; was there an error?
  1699.  
  1700.     jnc    cf_noopenerr
  1701.  
  1702. cf_cantread:    call    newline            ; couldn't open; print
  1703.     mov    si,offset CantRead    ; an error message
  1704.     call    putstr
  1705.     mov    ah,ERR_CopyProblem
  1706.     call    exit            ; and terminate
  1707.  
  1708. cf_noopenerr:    mov    sourceh,ax        ; save the handle
  1709.  
  1710.     mov    ah,CreateHandle        ; open
  1711.     mov    dx,offset destfile    ; desination
  1712.     xor    cx,cx            ; no attribute
  1713.     int    INTDOS
  1714.  
  1715.     jnc    cf_nowriteopen
  1716.  
  1717. cf_cantwrite:    call    newline            ; couldn't write; print
  1718.     mov    si,offset CantWrite    ; an error message
  1719.     call    putstr
  1720.     mov    ah,ERR_CopyProblem
  1721.     call    exit            ; and terminate!
  1722.  
  1723. cf_nowriteopen:    mov    desth,ax
  1724.  
  1725. cf_nextbunch:    mov    ah,ReadHandle        ; read
  1726.     mov    cx,CopyBufferLen    ; all we can
  1727.     mov    bx,sourceh        ; from the source
  1728.     mov    dx,offset copybuffer    ; into the buffer
  1729.     int    INTDOS
  1730.  
  1731.     jnc    cf_canread        ; no read error?
  1732.  
  1733.     mov    dx,offset destfile    ; delete the dest file
  1734.     mov    ah,DeleteFile
  1735.     int    INTDOS
  1736.     jmp    short cf_cantread    ; print error and quit
  1737.  
  1738. cf_canread:
  1739.     and    ax,ax            ; (Was it zero?)
  1740.     je    cf_closeup        ; yes! go close the files
  1741.  
  1742.     mov    cx,ax            ; take what we read
  1743.     mov    ah,WriteHandle        ; and write it
  1744.     mov    dx,offset copybuffer    ; from the buffer
  1745.     mov    bx,desth        ; to the destination
  1746.     int    INTDOS
  1747.  
  1748.     jnc    cf_canwrite        ; write error!
  1749.  
  1750.     mov    dx,offset destfile    ; delete the dest file
  1751.     mov    ah,DeleteFile
  1752.     int    INTDOS
  1753.     jmp    short cf_cantwrite    ; print error and quit
  1754.  
  1755. cf_canwrite:
  1756.     cmp    cx,ax            ; did we write 'em all?
  1757.     jne    cf_cantwrite        ; no?  that's a problem!
  1758.     je    cf_nextbunch
  1759.  
  1760. cf_closeup:
  1761.     mov    ax,GetDateTime        ; get the date
  1762.     mov    bx,sourceh        ; from the source file
  1763.     int    INTDOS
  1764.  
  1765.     mov    ax,SetDateTime        ; set the date
  1766.     mov    bx,desth        ; to the dest file
  1767.     int    INTDOS
  1768.  
  1769.     mov    bx,desth        ; close dest file
  1770.     mov    ah,CloseHandle
  1771.     int    INTDOS
  1772.  
  1773.     mov    bx,sourceh        ; close source file
  1774.     mov    ah,CloseHandle
  1775.     int    INTDOS
  1776.  
  1777.     ;    get the source file's attribute byte
  1778.  
  1779.     mov    dx,offset sourcefile
  1780.     mov    ax,GetAttrib
  1781.     int    INTDOS
  1782.  
  1783.     ;    set the dest file's attribute byte
  1784.  
  1785.     mov    dx,offset destfile
  1786.     mov    ax,SetAttrib        ; CX is already set
  1787.     or    cx,020h            ; but set archive bit
  1788.     int    INTDOS
  1789.     
  1790.     ;    set the source file's attribute byte to normal
  1791.  
  1792.     mov    dx,offset sourcefile
  1793.     mov    ax,SetAttrib
  1794.     mov    cx,NormalAttrib        ; reset source file
  1795.                     ; so we can now
  1796.     mov    dx,offset sourcefile    ; delete the source file
  1797.     mov    ah,DeleteFile
  1798.     int    INTDOS
  1799.  
  1800.     ret
  1801. copyfile    endp
  1802.  
  1803.  
  1804. ; ----------------------------------------------------------------------------
  1805. ; getyesno
  1806. ; This procedure prints a yes/no prompt on the screen and gets an answer.
  1807. ; the routine accepts "y", "1", and "t" as affirmative responses.  the
  1808. ; routine accepts "n", "0", and "f" as negative responses.  If the routine
  1809. ; is given an affirmative response, it will return AX as 0001.  If it is
  1810. ; given a negative reply, it will send back 0000 in AX.  If the routine
  1811. ; receives "a" in reply to its prompt, it will return a 0002 in AX.
  1812.  
  1813. getyesno    proc    near
  1814.  
  1815.     mov    si,offset ConfirmPrompt    ; print prompt
  1816.     call    putstr
  1817.  
  1818. gyn_delete:    xor    ax,ax
  1819.     mov    confirmreply,al
  1820.  
  1821. gyn_loop:    mov    ah,GetChar    ; get one character
  1822.     int    INTDOS
  1823.     cmp    al,'a'
  1824.     jl    gyn_noup
  1825.     cmp    al,'z'
  1826.     jg    gyn_noup
  1827.  
  1828.     sub    al,('a'-'A')    ; make it uppercase
  1829.  
  1830. gyn_noup:    mov    ah,confirmreply
  1831.     and    ah,ah
  1832.     jne    gyn_checkcr    ; we got a letter, is this one
  1833.                 ; a carriage return?
  1834.  
  1835.     cmp    al,cr        ; carriage return?
  1836.     je    gyn_negative    ; yep, default is no!
  1837.  
  1838.     cmp    al,ctrlz    ; control zee?
  1839.     jne    gyn_notzee
  1840.  
  1841.     ; if CTRL+Z is pressed, we must quit here.
  1842.  
  1843.     mov    si,offset UAborted
  1844.     call    putstr        ; make a message about it
  1845.     mov    al,ERR_None    ; exit with no errors
  1846.     call    exit
  1847.  
  1848. gyn_notzee:    cmp    al,space    ; is this character lower than ' '?
  1849.     jle    gyn_loop    ; yep, ignore it
  1850.  
  1851.     cmp    al,'Y'        ; filter out unwanted chars
  1852.     je    gyn_okay    ;
  1853.  
  1854.     cmp    al,'N'
  1855.     je    gyn_okay
  1856.  
  1857.     cmp    al,'A'
  1858.     je    gyn_okay
  1859.  
  1860.     cmp    al,'0'
  1861.     je    gyn_okay
  1862.  
  1863.     cmp    al,'1'
  1864.     je    gyn_okay
  1865.  
  1866.     cmp    al,'T'
  1867.     je    gyn_okay
  1868.  
  1869.     cmp    al,'F'
  1870.     jne    gyn_loop
  1871.  
  1872. gyn_okay:
  1873.     mov    confirmreply,al    ; store the character
  1874.  
  1875.     mov    ah,PutChar    ; and echo it to the screen
  1876.     mov    dl,al
  1877.     int    INTDOS
  1878.     jmp    short gyn_loop    ; go loop back for more
  1879.  
  1880. gyn_checkcr:    cmp    al,cr        ; is this one a carriage return?
  1881.     je    gyn_replied    ; yes! process the reply
  1882.  
  1883.     cmp    al,bs        ; is this one a backspace?
  1884.     jne    gyn_loop    ; no! bad char, ignore it
  1885.  
  1886.     mov    ah,PutChar    ; put a backspace
  1887.     mov    dl,bs
  1888.     int    INTDOS
  1889.  
  1890.     mov    ah,PutChar    ; put a space to whiteout
  1891.     mov    dl,space    ; the character typed
  1892.     int    INTDOS
  1893.  
  1894.     mov    ah,PutChar    ; and another BS to fix cursor
  1895.     mov    dl,bs
  1896.     int    INTDOS
  1897.  
  1898.     jmp    gyn_delete
  1899.  
  1900. gyn_replied:    mov    al,confirmreply    ; get the reply, and decide what
  1901.                 ;    to do
  1902.  
  1903.     cmp    al,'A'
  1904.     je    gyn_everything
  1905.  
  1906.     cmp    al,'0'
  1907.     je    gyn_negative
  1908.     cmp    al,'F'
  1909.     je    gyn_negative
  1910.     cmp    al,'N'
  1911.     je    gyn_negative
  1912.  
  1913.     cmp    al,'Y'
  1914.     je    gyn_affirm
  1915.     cmp    al,'1'
  1916.     je    gyn_affirm
  1917.     cmp    al,'T'
  1918.     je    gyn_affirm
  1919.     jmp    gyn_loop    ; somehow a bad char got in
  1920.  
  1921.  
  1922. gyn_everything:    mov    ax,2        ; reply "all"
  1923.     ret
  1924.  
  1925. gyn_negative:    mov    ax,0        ; negative reply
  1926.     ret
  1927.  
  1928. gyn_affirm:    mov    ax,1        ; affirmative reply
  1929.     ret
  1930.  
  1931. getyesno    endp
  1932.  
  1933.  
  1934. ; ----------------------------------------------------------------------------
  1935. ; check4dir
  1936. ; this routine accepts DI as a pointer to a FileNameS.  It will check
  1937. ; to see if the file name section of the file name is actually a directory.
  1938. ; if it is, it will move the file name to the end of the path field and
  1939. ; make the file name null.  if not, it simply returns without changing
  1940. ; the structure.  We have to do this because DOS won't understand us if
  1941. ; we specify a subdirectory without tacking \*.* onto the end of it.
  1942. ; If we end up changing anything, AX is set to one.  If not, AX is zilch.
  1943. ;
  1944.  
  1945. check4dir    proc    near
  1946.  
  1947.     push    si        ; save the pointer
  1948.  
  1949.     mov    di,offset temppath
  1950.     call    strcpy        ; move drive to temppath
  1951.  
  1952.     mov    di,offset temppath
  1953.     add    si,path        ; get the path
  1954.     call    strcat
  1955.  
  1956.     mov    di,offset temppath
  1957.     pop    si
  1958.     push    si
  1959.     add    si,fname
  1960.     call    strcat        ; and finally add the filename
  1961.  
  1962.     mov    ah,FindFirst
  1963.     mov    dx,offset temppath
  1964.     mov    cx,SubDirAttrib    ; get directories only
  1965.     int    INTDOS
  1966.     jnc    c4d_worked
  1967.  
  1968.     ; return with al set to zero to indicate that
  1969.     ; we didn't change the filename
  1970.  
  1971.     xor    ax,ax        ; make a zilcher out of ax
  1972.     pop    si
  1973.     ret
  1974.  
  1975.     ; it *is* a subdir!
  1976.     ; concatenate the file to the path
  1977.  
  1978.  
  1979. c4d_worked:    pop    si
  1980.     push    si
  1981.     mov    di,si
  1982.     add    di,path
  1983.     add    si,fname
  1984.     call    strcat        ; add filename to path
  1985.  
  1986.     mov    si,offset BackSlash
  1987.     call    strcat        ; add backslash to path
  1988.     
  1989.     mov    si,offset Star    ; put a star into the filename
  1990.     pop    di
  1991.     push    di
  1992.     add    di,fname
  1993.     call    strcpy
  1994.  
  1995.     mov    si,offset DotStar
  1996.     pop    di
  1997.     push    di
  1998.     add    di,fext        ; and a ".*" into extension
  1999.     call    strcpy
  2000.     
  2001. c4d_end:    mov    ax,1        ; ax=1, success!
  2002.     pop    si        ; pop register and return!
  2003.     ret
  2004. check4dir    endp
  2005.  
  2006.  
  2007. ; ----------------------------------------------------------------------------
  2008. ; normpath
  2009. ; this proc accepts a pointer to a FileNameS in DS:SI.  The proc will
  2010. ; call DOS to have it normalize the path -- this simply means that the
  2011. ; procedure will resolve any relative (ie, including "." or "..")
  2012. ; file path references.  Note that the function call used here is
  2013. ; undocumented.
  2014.  
  2015. normpath    proc    near
  2016.  
  2017.     push    di        ; save the registers
  2018.     push    si
  2019.  
  2020.  
  2021.     mov    di,offset temppath
  2022.     call    strcpy        ; copy drive and path to
  2023.     add    si,path        ; a temporary area
  2024.     call    strcat
  2025.  
  2026.     mov    ah,NormalizePath
  2027.     mov    si,offset temppath
  2028.     mov    di,offset fixedpath
  2029.     int    INTDOS
  2030.  
  2031.     pop    si
  2032.     mov    di,si        ; copy the path back
  2033.     add    di,path
  2034.     mov    si,(offset fixedpath)+2
  2035.     call    strcpy
  2036.     
  2037.     pop    di        ; restore
  2038.     ret            ; and return
  2039. normpath    endp
  2040.  
  2041.  
  2042. ; ----------------------------------------------------------------------------
  2043. ; That's a wrap.  Written under the influence of Tom Petty, Rush, and REM.
  2044.  
  2045.     end
  2046.  
  2047.